//------------------------------------------------------------------------------
// Desc:
// Tabs: 3
//
// Copyright (c) 2007-2008 Novell, Inc. All Rights Reserved.
//
// This program and the accompanying materials are made available
// under, alternatively, the terms of:  a) the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html; or, b) the Apache License,
// Version 2.0 which accompanies this distribution as is available at
// www.opensource.org/licenses/apache2.0.php.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com.
//
// Author: Andrew Hodgkinson <ahodgkinson@novell.com>
//------------------------------------------------------------------------------

// **************************************************************************
// Desc: Constants
// **************************************************************************

//const Cc = Components.classes;
//const Ci = Components.interfaces;
//const Cu = Components.utils;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://infocard/IdentitySelectorDiag.jsm");
Cu.import("resource://infocard/InformationCardHelper.jsm");
Cu.import("resource://infocard/IdentitySelectorPrefs.jsm");
Cu.import("resource://infocard/InformationCardDragAndDrop.jsm");

var nsIX509Cert = Ci.nsIX509Cert;

var nsIX509CertDB = Ci.nsIX509CertDB;
var nsX509CertDB = "@mozilla.org/security/x509certdb;1";

var nsINSSCertCache = Ci.nsINSSCertCache;
var nsNSSCertCache = "@mozilla.org/security/nsscertcache;1";

var nsICertTree = Ci.nsICertTree;
var nsCertTree = "@mozilla.org/security/nsCertTree;1";

var nsILocalFile = Ci.nsILocalFile;
var nsLocalFile = "@mozilla.org/file/local;1";

var nsIFileOutputStream = Ci.nsIFileOutputStream;
var nsFileOutputStream = "@mozilla.org/network/file-output-stream;1";

var nsICardObjTypeStr = "application/x-informationcard";
               
// **************************************************************************
// Desc: Global services
// **************************************************************************

var gLastFailedGetTokenDate = null;
var gDisableStartTime = null;
var gSelectorRegistry = [];

// **************************************************************************
// Desc:
// **************************************************************************

var IdentitySelector =
{
        // ***********************************************************************
        // Method: onInstall
        // ***********************************************************************
       
        onInstall : function( event)
        {
                var handlerAlreadyInstalled = false;
               
                try
                {
                        // Remove the load event listener
                       
                        window.removeEventListener( "load",
                                IdentitySelector.onInstall, false);
                               
                        // Determine if another add-on or plug-in has registered to handle
                        // information cards
                       
                        if( navigator.mimeTypes && navigator.mimeTypes.length)
                        {
                                var mimeHandler = navigator.mimeTypes[ nsICardObjTypeStr];
                               
                                if( mimeHandler && mimeHandler.enabledPlugin)
                                {
                                        handlerAlreadyInstalled = true;
                                }
                        }
                       
                        if( !handlerAlreadyInstalled)
                        {
                                var availEvent = document.createEvent( "Event");

                                availEvent.initEvent( "IdentitySelectorAvailable", true, true);
                                top.dispatchEvent( availEvent);
                               
                                if( top.hasOwnProperty("IdentitySelectorAvailable") && top.IdentitySelectorAvailable === true)
                                {
                                        handlerAlreadyInstalled = true;
                                }
                        }
                       
                        if( !handlerAlreadyInstalled)
                        {
                                // Add event handlers.  The optional fourth parameter to
                                // addEventListener indicates that the listener is willing to
                                // accept untrusted events (such as those generated by web
                                // content).
                               
                                window.addEventListener(
                                        "IdentitySelectorAvailable",
                                        IdentitySelector.onIdentitySelectorAvailable, false, true);
               
                                window.addEventListener(
                                        "CallIdentitySelector",
                                        IdentitySelector.onCallIdentitySelector, false, true);
                       
                                window.addEventListener(
                                        "ICHideNotificationBox",
                                        IdentitySelector.onHideNotificationBox, false, true);
                       
                                window.addEventListener(
                                        "ICObjectLoaded",
                                        IdentitySelector.onICardObjectLoaded, false, true);
               
                                window.addEventListener(
                                        "ICElementLoaded",
                                        IdentitySelector.onICardElementLoaded, false, true);
                                       
                                window.addEventListener(
                                        "ICFormSubmit",
                                        IdentitySelector.onFormSubmit, false, true);
                                       
                                window.addEventListener(
                                        "ICGetTokenValue",
                                        IdentitySelector.onGetTokenValue, false, true);
                                       
                                window.addEventListener(
                                        "ICProcessItems",
                                        IdentitySelector.onProcessItems, false, true);
                                       
                                window.addEventListener(
                                        "ICDOMChanged",
                                        IdentitySelector.onDOMChanged, false, true);
                                       
                                // Add a progress listener
                                       
                                window.getBrowser().addProgressListener(ICProgressListener);
                                
                                var observerService = Cc["@mozilla.org/observer-service;1"].
                                  getService(Ci.nsIObserverService);
                                observerService.addObserver(IdentitySelector._formSubmitObserver, "earlyformsubmit", false);

                        }
                        else
                        {
                                IdentitySelectorDiag.logMessage( "onInstall",
                                        "Another identity selector is already installed.");
                        }
                }
                catch( e)
                {
                        IdentitySelectorDiag.throwError( "onInstall", e);
                }
        },
       
        // ***********************************************************************
        // Method: onUninstall
        // ***********************************************************************
       
        onUninstall : function( event)
        {
                try
                {
                        // Remove the event listeners
                       
                        window.removeEventListener( "load",
                                IdentitySelector.onInstall, false);
                               
                        window.removeEventListener( "unload",
                                IdentitySelector.onUninstall, false);
                               
                        window.removeEventListener( "CallIdentitySelector",
                                IdentitySelector.onCallIdentitySelector, false);
               
                        window.removeEventListener( "IdentitySelectorAvailable",
                                IdentitySelector.onIdentitySelectorAvailable, false);
       
                        window.removeEventListener( "ICHideNotificationBox",
                                IdentitySelector.onHideNotificationBox, false);
               
                        window.removeEventListener( "ICObjectLoaded",
                                IdentitySelector.onICardObjectLoaded, false);
       
                        window.removeEventListener( "ICElementLoaded",
                                IdentitySelector.onICardElementLoaded, false);
                               
                        window.removeEventListener( "ICFormSubmit",
                                IdentitySelector.onFormSubmit, false);
                               
                        window.removeEventListener( "ICGetTokenValue",
                                IdentitySelector.onGetTokenValue, false);
                               
                        window.removeEventListener( "ICProcessItems",
                                IdentitySelector.onProcessItems, false);
                                       
                        window.removeEventListener( "ICDOMChanged",
                                IdentitySelector.onDOMChanged, false);
                               
                        // Remove progress listener
                                       
                        window.getBrowser().removeProgressListener( ICProgressListener);
                }
                catch( e)
                {
                        IdentitySelectorDiag.throwError( "onUninstall", e);
                }
        },
       
        // ***********************************************************************
        // Method: registerSelector
        // ***********************************************************************
       
        registerSelector : function( selector)
        {
                var     bFound = false;
               
                for( var iLoop = 0; iLoop < gSelectorRegistry.length; iLoop++)
                {
                        if( gSelectorRegistry[ iLoop].guid() === selector.guid())
                        {
                                bFound = true;
                                break;
                        }
                }
               
                if( !bFound)
                {
                  IdentitySelectorDiag.logMessage( "registerSelector",
                            "registering " + selector.guid());
                        gSelectorRegistry.push( selector);
                }
        },

        // ***********************************************************************
        // Method: deregisterSelector
        // ***********************************************************************
       
        deregisterSelector : function( selector)
        {
                for( var iLoop = 0; iLoop < gSelectorRegistry.length; iLoop++)
                {
                        if( gSelectorRegistry[ iLoop].guid() === selector.guid())
                        {
                                gSelectorRegistry.splice( iLoop, 1);
                                break;
                        }
                }
        },
       
        // ***********************************************************************
        // Method: runInterceptScript
        // ***********************************************************************
       
        runInterceptScript : function( doc)
        {
          var unWrappedDoc = doc;
          if( doc.wrappedJSObject)
          {
                  unWrappedDoc = doc.wrappedJSObject;
          }

          if( unWrappedDoc.__identityselector__ === undefined)
          {
                  // Load and execute the script
                  Cc[
                          "@mozilla.org/moz/jssubscript-loader;1"].getService(
                          Ci.mozIJSSubScriptLoader).loadSubScript(
                          "chrome://infocard/content/IdentitySelectorIntercept.js", doc);

                  IdentitySelectorDiag.logMessage( "runInterceptScript",
                          "Executed script on " + doc.location);
          }
        },
       
        // ***********************************************************************
        // Method: onProcessItems
        // ***********************************************************************
       
        onProcessItems : function( event)
        {
                var target = event ? event.target : this;
                var doc;
               
                if( target.wrappedJSObject)
                {
                        target = target.wrappedJSObject;
                }
               
                doc = target;
               
                // Sanity checks

                try
                {
                        // If the intercept script was executed correctly,
                        // __identityselector__ should be defined.
                       
                        if( doc.__identityselector__ === undefined)
                        {
                                // Since there are information card items on the page, warn
                                // the user that the identity selector was unable to insert
                                // itself properly.  This is most likely due to JavaScript being
                                // disabled.
                               
                                if( !IdentitySelectorUtil.isJavaScriptEnabled())
                                {
                                        IdentitySelectorDiag.reportError( "onProcessItems",
                                                "This page contains information card objects, but " +
                                                "JavaScript is disabled.  The information card " +
                                                "selector may not run properly.  To enable, go to " +
                                                "Preferences->Content->Enable JavaScript.");
                                }
                                else
                                {
                                        IdentitySelectorDiag.reportError( "onProcessItems",
                                                "This page contains information card objects, but " +
                                                "the information card selector was unable to fully " +
                                                "process the page.");
                                }
                               
                                // Hide the notification box
                               
                                IdentitySelector.onHideNotificationBox();
                               
                                // Done
                               
                                return;
                        }
                       
                        // Process all of the information card objects and elements
                        // in the document
               
                        doc.__identityselector__.contentLoaded = true;
                       
                        if( !doc.__identityselector__.submitIntercepted)
                        {
                                IdentitySelector.processICardItems( doc, true);
                        }
                }
                catch( e)
                {
                        IdentitySelectorDiag.debugReportError( "onProcessItems", e);
                }
        },
        
        // ***********************************************************************
        // Method: processICardItems
        // ***********************************************************************
       
        processICardItems : function( doc, dispatchEvents)
        {
          if (dispatchEvents) {
            var f = function(objElem, doc, arg) {};
          } else {
            var f = function(objElem, doc, arg) {
              var event = doc.createEvent( "Event");
              event.initEvent( arg, true, true);
              objElem.dispatchEvent( event);
            };
          }
          IdentitySelector.forEachICardItems(doc, f);
        },
        
        // ***********************************************************************
        // Method: processICardItems
        // ***********************************************************************
       
        forEachICardItems : function( doc, callback)
        {
                try
                {
                        var iLoop;
                        var itemCount = 0;
                       
                        // Process all of the information card objects in the document
                                       
                        var objElems = doc.getElementsByTagName( "OBJECT");
                        var icardObjectCount = 0;
                       
                        IdentitySelectorDiag.logMessage( "processICardItems", "Found " +
                                objElems.length + " object(s) on " + doc.location);
                               
                        for( iLoop = 0; iLoop < objElems.length; iLoop++)
                        {
                                var objElem = objElems[ iLoop];
                                var objTypeStr = objElem.getAttribute( "TYPE");
                               
                                if( (objTypeStr !== null &&
                                                objTypeStr.toLowerCase() == nsICardObjTypeStr) ||
                                        objElem._type == nsICardObjTypeStr)
                                {
                                        callback(objElem, doc, "ICObjectLoaded");
                                       
                                        icardObjectCount++;
                                }
                        }
                       
                        IdentitySelectorDiag.logMessage( "processICardItems", "Found " +
                                icardObjectCount + " ICard object(s) on " + doc.location);
                               
                        // Process all of the information card elements in the document
                       
                        var icardElems = doc.getElementsByTagName( "IC:INFORMATIONCARD");
                        var icardElementCount = icardElems.length;
                       
                        for( iLoop = 0; iLoop < icardElems.length; iLoop++)
                        {
                                var icardElem = icardElems[ iLoop];
                                
                                callback(icardElem, doc, "ICElementLoaded");
                        }
                       
                        IdentitySelectorDiag.logMessage( "processICardItems", "Found " +
                                icardElementCount + " ICard element(s) on " + doc.location);
                               
                        return( icardObjectCount + icardElementCount);
                }
                catch( e)
                {
                        IdentitySelectorDiag.debugReportError( "processICardItems", e);
                }
        },
       
        // ***********************************************************************
        // Method: setParamsFromElem
        // ***********************************************************************
       
        setParamsFromElem : function( doc, objElem)
        {
                try
                {
                        var data = doc.__identityselector__.data;
                       
                        // recipient
                       
                        if( doc.location.href !== undefined &&
                                doc.location.href !== null && doc.location.href !== "")
                        {
                                data.recipient = doc.location.href;
                        }
                        else
                        {
                                delete data.recipient;
                        }

                        // tokenType
                       
                        if( objElem.tokenType !== undefined && objElem.tokenType !== null)
                        {
                                data.tokenType = objElem.tokenType;
                        }
                        else
                        {
                                delete data.tokenType;
                        }
                       
                        // optionalClaims
                       
                        if( objElem.optionalClaims !== undefined &&
                                objElem.optionalClaims !== null)
                        {
                          data.optionalClaims = objElem.optionalClaims.replace(/\s+/g, ' ');
                          IdentitySelectorDiag.logMessage("setParamsFromElem", "optionalClaims=" +data.optionalClaims);
                        }
                        else
                        {
                                delete data.optionalClaims;
                        }
                       
                        // requiredClaims
                       
                        if( objElem.requiredClaims !== undefined &&
                                objElem.requiredClaims !== null)
                        {
                          data.requiredClaims = objElem.requiredClaims.replace(/\s+/g, ' ');
                          IdentitySelectorDiag.logMessage("setParamsFromElem", "requiredClaims=" +data.requiredClaims);
                        }
                        else
                        {
                                delete data.requiredClaims;
                        }
                       
                        // issuer
                       
                        if( objElem.issuer !== undefined && objElem.issuer !== null)
                        {
                                data.issuer = objElem.issuer;
                        }
                        else
                        {
                                delete data.issuer;
                        }
                       
                        // issuerPolicy
                       
                        if( objElem.issuerPolicy !== undefined && objElem.issuerPolicy !== null)
                        {
                                data.issuerPolicy = objElem.issuerPolicy;
                        }
                        else
                        {
                                delete data.issuerPolicy;
                        }
                       
                        // privacyUrl
                       
                        if( objElem.privacyUrl !== undefined &&
                                objElem.privacyUrl !== null)
                        {
                                data.privacyUrl = objElem.privacyUrl;
                        }
                        else
                        {
                                delete data.privacyUrl;
                        }
                       
                        // privacyVersion
       
                        if( objElem.privacyVersion !== undefined &&
                                objElem.privacyVersion !== null)
                        {
                                data.privacyVersion = objElem.privacyVersion;
                        }
                        else
                        {
                                delete data.privacyVersion;
                        }
                }
                catch( e)
                {
                        IdentitySelectorDiag.debugReportError( "setParamsFromElem", e);
                }
        },
        
        _handleFormSubmitForEmbedded : function(doc, icardElem, form) {
          IdentitySelectorDiag.logMessage( "onFormSubmit",
                  "Intercepted submit of form with embedded ICard element");
         
          // Process the embedded element
         
          if( !doc.__identityselector__.contentLoaded)
          {
                  var elementEvent = doc.createEvent( "Event");
                  elementEvent.initEvent( "ICElementLoaded", true, true);
                  icardElem.dispatchEvent( elementEvent);
          }
         
          // If the embedded ICard element doesn't have a token attached to
          // it, invoke the selector
         
          if( icardElem.token === undefined) {
            IdentitySelectorDiag.logMessage( "onFormSubmit",
                    "Submit encountered in-line");
           
            var selectorEvent = doc.createEvent( "Event");
            selectorEvent.initEvent( "CallIdentitySelector", true, true);
            doc.__identityselector__.targetElem = icardElem;
            doc.dispatchEvent( selectorEvent);
           
          }
        },
        
        _handleFormSubmitForObject : function(doc, objElem, form) {

          IdentitySelectorDiag.logMessage( "_handleFormSubmitForObject",
                  "Intercepted submit of form with embedded ICard object");
                 
          // Process the embedded object
         
          if( !doc.__identityselector__.contentLoaded)
          {
                  var objectEvent = doc.createEvent( "Event");
                  objectEvent.initEvent( "ICObjectLoaded", true, true);
                  objElem.dispatchEvent( objectEvent);
          }
         
          // If the embedded ICard object doesn't have a token attached to
          // it, invoke the selector
         
          if( objElem.token === undefined)
          {
                  IdentitySelectorDiag.logMessage( "_handleFormSubmitForObject",
                          "Submit encountered in-line");
                 
                  var selectorEvent = doc.createEvent( "Event");
                  selectorEvent.initEvent( "CallIdentitySelector", true, true);
                  doc.__identityselector__.targetElem = objElem;
                  doc.dispatchEvent( selectorEvent);
          }
        },
        
        _openIdStore : function(doc) {
          IdentitySelectorDiag.logMessage( "_openIdStore", "start");
          if( doc.wrappedJSObject)
          {
            doc = doc.wrappedJSObject;
          }
          IdentitySelectorDiag.logMessage( "_openIdStore", "doc.location.href=" + doc.location.href);
          IdentitySelectorDiag.logMessage( "_openIdStore", "doc.__identityselector__.openid=" + 
              doc.__identityselector__.openid);
        },
        
        _openIdLoaded : {
          _pwmgr : null,

          QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
                                                  Ci.nsISupportsWeakReference]),


          handleEvent : function (event) {
              if (!event.isTrusted)
                  return;

              IdentitySelectorDiag.logMessage( "domEventListener", "got event " + event.type);

              switch (event.type) {
                  case "DOMContentLoaded":
                    IdentitySelector._openIdStore(event.target);
                      //this._pwmgr._fillDocument(event.target);
                      return;

//                  case "DOMAutoComplete":
//                  case "blur":
//                      var acInputField = event.target;
//                      var acForm = acInputField.form;
//
//                      // If the username is blank, bail out now -- we don't want
//                      // fillForm() to try filling in a login without a username
//                      // to filter on (bug 471906).
//                      if (!acInputField.value)
//                          return;
//
//                      // Make sure the username field fillForm will use is the
//                      // same field as the autocomplete was activated on. If
//                      // not, the DOM has been altered and we'll just give up.
//                      var [usernameField, passwordField, ignored] =
//                          this._pwmgr._getFormFields(acForm, false);
//                      if (usernameField == acInputField && passwordField) {
//                          // This shouldn't trigger a master password prompt,
//                          // because we don't attach to the input until after we
//                          // successfully obtain logins for the form.
//                          this._pwmgr._fillForm(acForm, true, true, true, null);
//                      } else {
//                          this._pwmgr.log("Oops, form changed before AC invoked");
//                      }
//                      return;

                  default:
                    IdentitySelectorDiag.logMessage( "domEventListener", "Oops! This event unexpected.");
                    return;
              }
          }
      },
        
        // ***********************************************************************
        // Method: onEarlyFormSubmit
        // ***********************************************************************

        onEarlyFormSubmit : function(formElement, aWindow, actionURI)
        {
          IdentitySelectorDiag.logMessage( "onEarlyFormSubmit", "Intercepted submit " + actionURI.spec);
          var objElem;
          var icardElem;
          var doc = formElement.ownerDocument;
          if( doc.wrappedJSObject)
          {
            doc = doc.wrappedJSObject;
          }

          if (doc.__identityselector__.submitIntercepted === true) {
            IdentitySelectorDiag.logMessage( "onEarlyFormSubmit", 
                "already handled form submit in onFormSubmit");
            return true; // just submit the form 
          }
          
          IdentitySelectorDiag.logMessage( "onEarlyFormSubmit", 
              "doc.__identityselector__.submitIntercepted=" + doc.__identityselector__.submitIntercepted);
          
          IdentitySelectorDiag.logMessage( "onEarlyFormSubmit", "doc.location.href=" + doc.location.href);
          
          // Determine if the form has an embedded information card object.
          var input;
          if( (objElem = IdentitySelector.getEmbeddedICardObject( formElement)) !== null)
          {
            doc.__identityselector__.submitIntercepted = true;
            IdentitySelector._handleFormSubmitForObject(doc, objElem, formElement);
            // If a token was retrieved, add it as a hidden field of the form
            if(objElem.token && !objElem.preventDefault)
            {
              input = doc.createElement( "INPUT");   
              input.setAttribute( "name",
                  objElem.getAttribute( "name"));
              input.setAttribute( "type", "hidden");
              input.value = objElem.token;
              formElement.appendChild( input);
            } else {
              IdentitySelectorDiag.logMessage( "onEarlyFormSubmit", "objElem.token is null or undefined: preventDefault");
              return false; // do not submit the form
            } 
          } else {
              if( (icardElem = IdentitySelector.getEmbeddedICardElement( formElement)) !== null) {
                doc.__identityselector__.submitIntercepted = true;
                IdentitySelector._handleFormSubmitForEmbedded(doc, icardElem, formElement);
                // If a token was retrieved, add it as a hidden field of the form
                if(icardElem.token && !icardElem.preventDefault)
                {
                  input = doc.createElement( "INPUT");   
                  input.setAttribute( "name",
                          icardElem.getAttribute( "name"));
                  input.setAttribute( "type", "hidden");
                  input.value = icardElem.token;
                  formElement.appendChild( input);
                } else {
                  IdentitySelectorDiag.logMessage( "onEarlyFormSubmit", "icardElem.token is null or undefined: preventDefault");
                  return false; // do not submit the form
                } 

              } else {
                var openidElement = IdentitySelector.getEmbeddedOpenidElement( formElement);
                if(openidElement) {
                  IdentitySelectorDiag.logMessage( "onEarlyFormSubmit", "Intercepted submit of OPENID form openid=" + 
                      openidElement.value);
                  doc.__identityselector__.openid = openidElement.value;
                  aWindow.addEventListener("DOMContentLoaded", IdentitySelector._openIdLoaded, false);
                } else {
                  IdentitySelectorDiag.logMessage( "onEarlyFormSubmit", "Intercepted submit of standard form");
                }
              }
          }
          return true;
        },
        
        // ***********************************************************************
        // Method: onFormSubmit
        // 
        // This is called BEFORE earlyformsubmit IF it is called
        //
        // ***********************************************************************

        onFormSubmit : function( event)
        {
                try
                {
                        var target = event ? event.target : this;
                        var doc;
                        var form;
                        var objElem;
                        var icardElem;
                        var selectorEvent;
                        var input;
                       
                        if( target.wrappedJSObject)
                        {
                                target = target.wrappedJSObject;
                        }
                       
                        form = target;
                        doc = target.ownerDocument;
                        if (!doc) {
                          IdentitySelectorDiag.reportError("onFormsubmit: !doc");
                          return;
                        }
                        
                        IdentitySelectorDiag.logMessage( "onFormSubmit", 
                            "doc.__identityselector__.submitIntercepted=" + doc.__identityselector__.submitIntercepted);
                        if (doc.__identityselector__.submitIntercepted) {
                          IdentitySelectorDiag.logMessage( "onFormSubmit", "form submit was already handled");
                          event.preventDefault(); // do not submit the form
                          return;
                        }

                        // Determine if the form has an embedded information card object.
                       
                        if( (objElem = IdentitySelector.getEmbeddedICardObject( form)) !== null)
                        {
                          IdentitySelector._handleFormSubmitForObject(doc, objElem, form);
                          // If a token was retrieved, add it as a hidden field of the form
                          if(objElem.token && !objElem.preventDefault)
                          {
                            input = doc.createElement( "INPUT");   
                            input.setAttribute( "name",
                                objElem.getAttribute( "name"));
                            input.setAttribute( "type", "hidden");
                            input.value = objElem.token;
                            form.appendChild( input);
                          } else {
                            IdentitySelectorDiag.logMessage( "onFormSubmit", "objElem.token is null or undefined: preventDefault");
                            event.preventDefault(); // do not submit the form
                          } 
                        }
                        else if( (icardElem =
                                IdentitySelector.getEmbeddedICardElement( form)) !== null)
                        {
                          IdentitySelector._handleFormSubmitForEmbedded(doc, icardElem, form);
                          // If a token was retrieved, add it as a hidden field of the form
                          if(icardElem.token && !icardElem.preventDefault)
                          {
                            input = doc.createElement( "INPUT");   
                            input.setAttribute( "name",
                                    icardElem.getAttribute( "name"));
                            input.setAttribute( "type", "hidden");
                            input.value = icardElem.token;
                            form.appendChild( input);
                          } else {
                            IdentitySelectorDiag.logMessage( "onFormSubmit", "icardElem.token is null or undefined: preventDefault");
                            event.preventDefault(); // do not submit the form
                          } 
                        }
                        else
                        {
                                IdentitySelectorDiag.logMessage( "onFormSubmit",
                                        "Intercepted submit of standard form");
                        }
                       
                        doc.__identityselector__.submitIntercepted = true;
                }
                catch( e)
                {
                        IdentitySelectorDiag.debugReportError( "onFormSubmit", e);
                }
        },

        // ***********************************************************************
        // Method: onGetTokenValue
        // ***********************************************************************
       
        onGetTokenValue : function( event)
        {
                try
                {
                        var target = event ? event.target : this;
                        var doc;
                        var targetElem;
                       
                        if( target.wrappedJSObject)
                        {
                                target = target.wrappedJSObject;
                        }
                       
                        targetElem = target;
                        doc = target.ownerDocument;
                       
                        IdentitySelectorDiag.logMessage( "onGetTokenValue",
                                "Token value requested");
                               
                        if( targetElem.__value === undefined)
                        {
                                var selectorEvent = doc.createEvent( "Event");
                               
                                selectorEvent.initEvent( "CallIdentitySelector", true, true);
                                doc.__identityselector__.targetElem = targetElem;
                                doc.dispatchEvent( selectorEvent);
                                targetElem.__value = targetElem.token;
                        }
                }
                catch( e)
                {
                        IdentitySelectorDiag.throwError( "onGetTokenValue", e);
                }
        },
       
        // ***********************************************************************
        // Method: extractParameter
        // ***********************************************************************
       
        extractParameter : function( sourceNode, dataObj)
        {
                switch( sourceNode.name.toLowerCase())
                {
                        case "tokentype":
                                IdentitySelectorDiag.logMessage( "extractParameter",
                                        "tokenType = " + sourceNode.value);
                                dataObj.tokenType = sourceNode.value;
                                break;
                 
                        case "optionalclaims":
                                IdentitySelectorDiag.logMessage( "extractParameter",
                                        "optionalClaims = " + sourceNode.value);
                                dataObj.optionalClaims = sourceNode.value;
                                break;
         
                        case "requiredclaims":
                                IdentitySelectorDiag.logMessage( "extractParameter",
                                        "requiredClaims = " + sourceNode.value);
                                dataObj.requiredClaims = sourceNode.value;
                                break;
         
                        case "issuer":
                                IdentitySelectorDiag.logMessage( "extractParameter",
                                        "issuer = " + sourceNode.value);
                                dataObj.issuer = sourceNode.value;
                                break;
         
                        case "issuerpolicy":
                                IdentitySelectorDiag.logMessage( "extractParameter",
                                        "issuerPolicy = " + sourceNode.value);
                                dataObj.issuerPolicy = sourceNode.value;
                                break;

                        case "privacyurl":
                                IdentitySelectorDiag.logMessage( "extractParameter",
                                        "privacyUrl = " + sourceNode.value);
                                dataObj.privacyUrl = sourceNode.value;
                                break;

                        case "privacyversion":
                                IdentitySelectorDiag.logMessage( "extractParameter",
                                        "privacyVersion = " + sourceNode.value);
                                dataObj.privacyVersion = sourceNode.value;
                                break;

                        default:
                                IdentitySelectorDiag.logMessage( "extractParameter",
                                        "unknown parameter: " + sourceNode.name +
                                        " = " + sourceNode.value);
                            try {
                              dataObj[sourceNode.name] = sourceNode.value;
                            } catch (ex) {
                              IdentitySelectorDiag.logMessage( "extractParameter", "Exception: " + ex);
                            }
                                break;
                }
        },

        // ***********************************************************************
        // Method: onDOMChanged
        // ***********************************************************************
       
        onDOMChanged : function( event)
        {
                var target = event ? event.target : this;
                var doc;

                if( target.wrappedJSObject)
                {
                        target = target.wrappedJSObject;
                }

                try
                {
                        if( (doc = target.ownerDocument) === undefined)
                        {
                                return;
                        }
                       
                        if( target instanceof HTMLObjectElement)
                        {
                                var objTypeStr = target.getAttribute( "TYPE");

                                IdentitySelectorDiag.logMessage( "onDOMChanged",
                                        "OBJECT changed, type = " + objTypeStr);

                                if( (objTypeStr !== null && objTypeStr.toLowerCase() ==
                                                "application/x-informationcard") ||
                                        target._type == "application/x-informationcard")
                                {
                                        delete target.__processed;
                                       
                                        var objectEvent = doc.createEvent( "Event");
                                        objectEvent.initEvent( "ICObjectLoaded", true, true);
                                        target.dispatchEvent( objectEvent);
                                }
                        }
                }
                catch( e)
                {
                        IdentitySelectorDiag.debugReportError( "onDOMChanged", e);
                }
        },
        
        ondrop : function(event)
        {
          var data = event.dataTransfer.getData("application/x-informationcard+id");
          if (data) {
            var target = event.target;
            var doc = target.ownerDocument;
            if( doc.wrappedJSObject)
            {
              doc = doc.wrappedJSObject;
            }
            doc.__identityselector__.cardId = data;

            IdentitySelectorDiag.logMessage( "onDrop",
                "id=" + data );
            IdentitySelectorDiag.logMessage( "onDrop",
                "target.tagName=" + target.tagName );
            var form = target;
            var theForm;
            while (form) {
              if (form.tagName != undefined && form.tagName == "FORM") {
                theForm = form;
                break;
              }
              form = form.parentNode;
            }
            if (!theForm) { return; }
            // we know that this form contains an object because otherwise
            // we would not be in this event handler
            event.preventDefault();
            // submit the form
            var evnt = doc.createEvent("Event");
            evnt.initEvent("submit", true, true);
            theForm.dispatchEvent(evnt);
          }
        }, 
        
        ondragover : function(event)
        {
          var forMe = event.dataTransfer.types.contains("application/x-informationcard+id");
          IdentitySelectorDiag.logMessage( "onDragOver", "forMe=" + forMe );
          if (forMe) {
            event.preventDefault();
          }
        },
        
        getSupportedFlavours : function () {
          var flavours = new FlavourSet();
          flavours.appendFlavour("application/x-informationcard+id");
          return flavours;
        },
        
        // ***********************************************************************
        // Method: onICardObjectLoaded
        // ***********************************************************************
       
        onICardObjectLoaded : function( event)
        {
          IdentitySelectorDiag.logMessage( "onICardObjectLoaded",
          "start. caller=" + IdentitySelector.onICardObjectLoaded.caller );

          var target = event ? event.target : this;
            var doc;
           
            if( target.wrappedJSObject)
            {
                    target = target.wrappedJSObject;
            }
           
            doc = target.ownerDocument;
           
            try
            {
                    var objElem = target;
                    var form = objElem;
                    var browser = getBrowser();
                   
                    if( objElem.__processed === true)
                    {
                            IdentitySelectorDiag.logMessage( "onICardObjectLoaded",
                                    "ICard object has already been processed.");
                    }
                    else
                    {
                            // Make sure the intercept script has been run on this document.
                            // In the case of an iframe, it may not have been run.
                           
                            if( doc.__identityselector__ === undefined)
                            {
                                    IdentitySelector.runInterceptScript( doc);
                            }
                           
                            // Override the value getter
                           
                            delete objElem.value;
                            
                            objElem.addEventListener("dragover", IdentitySelector.ondragover, false);
                            objElem.addEventListener("drop", IdentitySelector.ondrop, false);
                            
                            objElem.__defineGetter__( "value",
                                    doc.__identityselector__.valueGetter);
                                   
                            // Clear the type attribute if this version of the browser
                            // doesn't support hiding the notification box
                           
                            if( typeof( browser.getNotificationBox) != "function")
                            {
                                    if( (objElem._type = objElem.getAttribute(
                                            "type")) !== undefined)
                                    {
                                            objElem._type = objElem._type.toLowerCase();
                                            objElem.removeAttribute( "type");
                                    }
                            }
                                   
                            // Intercept all submit actions if the object is embedded within
                            // a parent form
                           
                            while( form !== null)
                            {
                                    if( form.tagName !== undefined && form.tagName == "FORM")
                                    {
                                            // The form submit method has been hooked.  We still need
                                            // to register for the submit event, however.
                                           
                                            form.addEventListener( "submit",
                                                    IdentitySelector.onFormSubmit, false);
                                            
                                            form.addEventListener("dragover", IdentitySelector.ondragover, false);
                                            form.addEventListener("drop", IdentitySelector.ondrop, false);
                                            break;
                                    }
                                   
                                    form = form.parentNode;
                            }
                           
                            // Set this as the default information card target element
                           
                            doc.__identityselector__.targetElem = objElem;
                           
                            // Log the event
                           
                            IdentitySelectorDiag.logMessage( "onICardObjectLoaded",
                                    "Processed ICard object.");
                                   
                            // Mark the object as 'processed'
                                   
                            objElem.__processed = true;
                            
                            objElem.hasCapability = function(capability) { return (capability === "openIdExperimental"); };
                    }
            }
            catch( e)
            {
                    IdentitySelectorDiag.debugReportError( "onICardObjectLoaded", e);
            }
           
            if( !("notificationBoxHidden" in doc.__identityselector__))
            {
                    IdentitySelector.onHideNotificationBox();
                    doc.__identityselector__.notificationBoxHidden = true;
            }
        },
       
        // ***********************************************************************
        // Method: onICardElementLoaded
        // ***********************************************************************
       
        onICardElementLoaded : function( event)
        {
                var target = event ? event.target : this;
                var doc;
               
                if( target.wrappedJSObject)
                {
                        target = target.wrappedJSObject;
                }
               
                doc = target.ownerDocument;
               
                try
                {
                        var icardElem = target;
                        var form = icardElem;
                       
                        if( icardElem.__processed === true)
                        {
                                IdentitySelectorDiag.logMessage( "onICardElementLoaded",
                                        "ICard element has already been processed.");
                        }
                        else
                        {
                                // Override the value getter
                               
                                delete icardElem.value;
                               
                                icardElem.__defineGetter__( "value",
                                        doc.__identityselector__.valueGetter);
                               
                                // Intercept all submit actions if the element is embedded within
                                // a parent form
                               
                                while( form !== null)
                                {
                                        if( form.tagName !== undefined && form.tagName == "FORM")
                                        {
                                                // The form submit method has been hooked.  We still need
                                                // to register for the submit event, however.
                                               
                                                form.addEventListener( "submit",
                                                        IdentitySelector.onFormSubmit, false);
                                                break;
                                        }
                                       
                                        form = form.parentNode;
                                }
                               
                                // Set this as the default information card element
                               
                                doc.__identityselector__.targetElem = icardElem;
                               
                                // Log the event
                               
                                IdentitySelectorDiag.logMessage( "onICardElementLoaded",
                                        "Processed ICard element.");
                                       
                                // Mark the element as 'processed'
                               
                                icardElem.__processed = true;
                                objElem.hasCapability = function(capability) { return (capability === "openIdExperimental"); };

                        }
                }
                catch( e)
                {
                        IdentitySelectorDiag.debugReportError( "onICardElementLoaded", e);
                }
        },
        
        getDer : function(cert,win){

          var length = {};
          var derArray = cert.getRawDER(length);
          var certBytes = '';
          for (var i = 0; i < derArray.length; i++) {
              certBytes = certBytes + String.fromCharCode(derArray[i]);
          }
          return win.btoa(certBytes);

        },

        // ***********************************************************************
        // Method: onCallIdentitySelector
        // ***********************************************************************
       
        onCallIdentitySelector : function( event)
        {
                var target = event ? event.target : this;
                var doc;
                var dataObj;
                var result;
                var identObject;
                var selectorClass;
                var optionalClaims = null;
                var requiredClaims = null;
                var currentTime = new Date();
                var iDOSThreshold = 15;
                var iLoop;
               
                // Log the operation
               
                IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                        "Identity selector invoked.");
                       
                // Configure the target
               
                if( target.wrappedJSObject)
                {
                        target = target.wrappedJSObject;
                }
               
                doc = target;
                identObject = doc.__identityselector__;
                dataObj = identObject.data;
               
                // Log information about the browser and platform
               
                IdentitySelectorDiag.logPlatformInfo();
               
                // Request a token from the selector
                       
                try
                {
                        if( identObject.targetElem !== null)
                        {
                          IdentitySelector.setParamsFromElem( doc, identObject.targetElem);
                          if (identObject.targetElem.token) {
                            if( identObject.targetElem.wrappedJSObject)
                            {
                              IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                              "identObject.targetElem is wrapped.");
                              identObject.targetElem = identObject.targetElem.wrappedJSObject;
                            }
                            try {
                              delete identObject.targetElem.token; // FIXME this fails since 20100430
                            } catch (deleteException) {
                              IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                "delete identObject.targetElem.token failed");
                            }
                          }
                        }
                        else
                        {
                                IdentitySelectorDiag.throwError( "onCallIdentitySelector",
                                        "Invalid target element.");
                        }
                       
                        if (identObject.cardid) {
                          dataObj.cardid = identObject.cardid;
                          IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                              "dataObj.cardid=" + dataObj.cardid);
                        }
                        
                        // Process parameters
                       
                        if( identObject.targetElem.tagName == "OBJECT")
                        {
                                var objElem = identObject.targetElem;
                               
                                // Process parameters
                               
                                if( objElem.childNodes.length)
                                {
                                        IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                                "Found " + objElem.childNodes.length +
                                                " object child elements");
                                               
                                        // Process the parameter values
                                       
                                        for(var eachNode=0; eachNode<objElem.childNodes.length; eachNode++)
                                        {
                                                var childNode = objElem.childNodes[ eachNode];
                                       
                                                if( childNode.tagName !== undefined)
                                                {
                                                        IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                                                "Processing object child element = " +
                                                                childNode.tagName);
       
                                                        if( childNode.tagName == "PARAM")
                                                        {
                                                                IdentitySelector.extractParameter( childNode, dataObj);
                                                        }
                                                }
                                        }
                                }
                        }
                        else
                        {
                                var icardElem = identObject.targetElem;
                               
                                for( iLoop = 0; iLoop < icardElem.attributes.length; iLoop++)
                                {
                                        IdentitySelector.extractParameter(
                                                icardElem.attributes[ iLoop], dataObj);
                                }
                               
                                // Process the child nodes
                               
                                var addElems = icardElem.getElementsByTagName( "IC:ADD");
                               
                                for( iLoop = 0; iLoop < addElems.length; iLoop++)
                                {
                                        var addElem = addElems[ iLoop];
                                        var claimTypeAttr = addElem.attributes.claimType;
                                        var optionalAttr = addElem.attributes.optional;
                                        var isOptional = false;
                                       
                                        if( claimTypeAttr !== null)
                                        {
                                                if( optionalAttr !== null)
                                                {
                                                        if( optionalAttr.value == "true")
                                                        {
                                                                isOptional = true;
                                                        }
                                                }
                                       
                                                if( isOptional)
                                                {
                                                        if( optionalClaims)
                                                        {
                                                                optionalClaims += " " + claimTypeAttr.value;
                                                        }
                                                        else
                                                        {
                                                                optionalClaims = claimTypeAttr.value;
                                                        }
                                                }
                                                else
                                                {
                                                        if( requiredClaims)
                                                        {
                                                                requiredClaims += " " + claimTypeAttr.value;
                                                        }
                                                        else
                                                        {
                                                                requiredClaims = claimTypeAttr.value;
                                                        }
                                                }
                                               
                                                IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                                        "claimType = " + claimTypeAttr.value);
                                        }
                                }
                               
                                if( optionalClaims)
                                {
                                        dataObj.optionalClaims = optionalClaims;
                                        IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                                "optionalClaims = " + optionalClaims);
                                }
                               
                                if( requiredClaims)
                                {
                                        dataObj.requiredClaims = requiredClaims;
                                        IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                                "requiredClaims = " + requiredClaims);
                                }
                        }
                       
                        // Get the selector 
//                        var aSelectorClasz = InformationCardHelper.getSelectorClass();
                        var aSelectorClasz = InformationCardHelper.getCardstore();
                        if (!aSelectorClasz) {
                          IdentitySelectorDiag.reportError( "onCallIdentitySelector",
                                    "Unable to locate an identity selector.  " +
                                    "Please make sure one is installed.");
                          return null;
                        }
                        // Set the token to null
                        identObject.targetElem.token = null;
                       
                        // Check for a denial-of-service attack
                        if( gDisableStartTime !== null)
                        {
                                // If there haven't been any token requests in the last 15 seconds,
                                // the selector can be re-enabled
                               
                                if( gDisableStartTime.getTime() +
                                         (iDOSThreshold * 1000) < currentTime.getTime())
                                {
                                        gDisableStartTime = null;
                                        gLastFailedGetTokenDate = null;
                                       
                                        IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                                "Selector re-enabled.");
                                }
                                else
                                {
                                        // Re-start the "disabled" timer
                                       
                                        gDisableStartTime = new Date();
                                }       
                        }
                        else
                        {
                                // Verify that it has been at least 10 seconds since the last
                                // time a token was requested
                               
                                if( gLastFailedGetTokenDate !== null)
                                {
                                        if( currentTime.getTime() <
                                                gLastFailedGetTokenDate.getTime() + (iDOSThreshold * 1000))
                                        {
                                                if( !window.confirm( "The identity selector was launched " +
                                                        "less than " + iDOSThreshold + " seconds ago.  This " +
                                                        "can sometimes be caused by a site attempting a " +
                                                        "denial-of-service attack.  Select 'Cancel' to " +
                                                        "temporarily disable the identity selector or 'OK' " +
                                                        "to allow the selector to launch."))
                                                {
                                                        currentTime = gDisableStartTime = new Date();
                                                }
                                        }
                                }
                        }
                               
                        // Invoke the selector
                       
                        if( gDisableStartTime !== null)
                        {
                                IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                        "Launching the selector has been disabled");
                                       
                                doc.getElementsByTagName( "HTML")[ 0].innerHTML =
                                        "<body>" +
                                        "<h2>The identity selector has been disabled.</h2>" +
                                        "<blockquote>The selector was disabled to prevent a " +
                                        "denial-of-service attack.  It will be re-enabled " +
                                        "automatically in " +
                                                ((((gDisableStartTime.getTime() + (iDOSThreshold * 1000)) -
                                                currentTime.getTime()) / 1000) | 0) +
                                         " second(s).</blockquote>" +
                                         "</body>";
                        }
                        else
                        {
                          gLastFailedGetTokenDate = null;
                          
                          var extraParams = function(){
                            var extraParams = [];
                            var len = 0;
                            for (var i in dataObj) {
                              if (dataObj.hasOwnProperty(i)) {
                                IdentitySelectorDiag.logMessage("onCallIdentitySelector ", 
                                    "data[" + i + "]=" + dataObj[i]); 
                                if (("issuer" !== ""+i) 
                                  && ("recipient" !== ""+i)
                                  && ("requiredClaims" !== ""+i)
                                  && ("optionalClaims" !== ""+i)
                                  && ("tokenType" !== ""+i)
                                  && ("privacyUrl" !== ""+i)
                                  && ("privacyVersion" !== ""+i)
                                  && ("issuerPolicy" !== ""+i)) 
                                {
                                  var obj = {};
                                  obj[i] = dataObj[i];
                                  len = extraParams.length;
                                  extraParams[len] = JSON.stringify(obj);
                                  IdentitySelectorDiag.logMessage("onCallIdentitySelector ",
                                      "extraParams[" + len + "] = " + extraParams[len]);
                                } else {
                                  IdentitySelectorDiag.logMessage("onCallIdentitySelector ",
                                      "i=" + i + "; value=" + dataObj[i] + ";");
                                }
                              }
                            }
                            return extraParams;
                          }();

                          if (doc.__identityselector__.cardId !== undefined) {
                            var aCardIdObject = {};
                            aCardIdObject.cardid = "" + doc.__identityselector__.cardId;
                            len = extraParams.length;
                            extraParams[len] = JSON.stringify(aCardIdObject);
                            IdentitySelectorDiag.logMessage("onCallIdentitySelector ",
                                "len=" + len + "; value=" + extraParams[len] + ";");
                          }

                          var browser = getBrowser();
                          var sslCert = InformationCardHelper.getSSLCertFromBrowser(browser);
                          if (!sslCert) {
                            IdentitySelectorDiag.logMessage("onCallIdentitySelector ",
                                "sslCert is null for doc.location.href=" + doc.location.href + 
                                " dataObj.recipient=" + dataObj.recipient);
                          }
                          

                          identObject.targetElem.token = aSelectorClasz.GetBrowserToken(dataObj.issuer,
                              dataObj.recipient, dataObj.requiredClaims,
                              dataObj.optionalClaims, dataObj.tokenType, dataObj.privacyUrl,
                              dataObj.privacyVersion, sslCert, dataObj.issuerPolicy,
                              extraParams.length, extraParams);
                          IdentitySelectorDiag.logMessage("onCallIdentitySelector ",
                              "identObject.targetElem.token=" + identObject.targetElem.token);
                          if (identObject.targetElem.token === null)
                          {
                                  gLastFailedGetTokenDate = new Date();
                          } else {
                            var tokenType = null;
                            if (dataObj.hasOwnProperty("tokenType")) {
                              tokenType = dataObj.tokenType;
                            }
                            // XMLDAP: tokenType: http://specs.openid.net/auth/2.0
                            if ("http://specs.openid.net/auth/2.0" === tokenType) {
                              // restrict this to http:// and https:// 
                            var myURI = Cc["@mozilla.org/network/io-service;1"]
                                                           .getService(Ci.nsIIOService)
                                                           .newURI(identObject.targetElem.token, null, null);
                            try {
                               var myURL = myURI.QueryInterface(Ci.nsIURL);
                            }
                            catch(uriException) {
                               // the URI is not an URL
                               IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                   'the URI is not an URL: ' + identObject.targetElem.token +
                                   "\n" + uriException);
                            }
                            if (myURI.schemeIs("http") || myURI.schemeIs("https")) {
                              IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                  'doc.location: ' + doc.location);
                              IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                  'stopping doc.location: ' + doc.location);
                              doc.defaultView.stop();
                              IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                  'Setting location of main window to: ' + identObject.targetElem.token);
                              doc.defaultView.location = identObject.targetElem.token;
                              IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                  'Setting preventDefault');
                              identObject.targetElem.preventDefault = true;
                            } else {
                              // Throw???
                              IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                  'Scheme of URI is not http or https! REJECTED: ' + 
                                   identObject.targetElem.token);
                            }
                            } else {
                              IdentitySelectorDiag.logMessage( "onCallIdentitySelector",
                                  "tokenType=" + tokenType);
                            }

                          }
                        }
                }
                catch( e)
                {
                        IdentitySelectorDiag.throwError( "onCallIdentitySelector", e);
                }
        },
       
        // ***********************************************************************
        // Method: onIdentitySelectorAvailable
        // ***********************************************************************
       
        onIdentitySelectorAvailable : function( event)
        {
                var target = event ? event.target : this;
               
                if( target.wrappedJSObject)
                {
                        target = target.wrappedJSObject;
                }

                target.IdentitySelectorAvailable = true;
        },
       
        // ***********************************************************************
        // Method: onHideNotificationBox
        // ***********************************************************************
       
        onHideNotificationBox : function( event)
        {
                try
                {
                        var browser = getBrowser();
                       
                        if( typeof( browser.getNotificationBox) == "function")
                        {
                                browser.getNotificationBox().notificationsHidden = true;
                        }
                }
                catch( e)
                {
                  IdentitySelectorDiag.reportError("onHideNotificationBox", "" + e);
                }
        },
       
        // ***********************************************************************
        // Method: getEmbeddedICardObject
        // ***********************************************************************
       
        getEmbeddedICardObject : function( targetElem)
        {
                var objElems = targetElem.getElementsByTagName( "OBJECT");
               
                for( var iLoop = 0; iLoop < objElems.length; iLoop++)
                {
                        var objElem = objElems[ iLoop];
                        var objTypeStr = objElem.getAttribute( "TYPE");
                       
                        if( (objTypeStr !== null &&
                                        objTypeStr.toLowerCase() == nsICardObjTypeStr) ||
                                objElem._type == nsICardObjTypeStr)
                        {
                                return( objElem);
                        }
                }
               
                return( null);
        },
       
        // ***********************************************************************
        // Method: getEmbeddedICardElement
        // ***********************************************************************
       
        getEmbeddedICardElement : function( targetElem)
        {
                var icardElems = targetElem.getElementsByTagName( "IC:INFORMATIONCARD");
               
                if( icardElems.length)
                {
                        return( icardElems[ 0]);
                }
               
                return( null);
        },
       
        // ***********************************************************************
        // Method: getEmbeddedOpenidElement
        // ***********************************************************************
       
        getEmbeddedOpenidElement : function(targetElem)
        {
//          IdentitySelectorDiag.logMessage("getEmbeddedOpenidElement", "start");
          var elts = targetElem.getElementsByTagName( "INPUT");
          if (!elts) return null;
          
          if (elts.length<1) return null;
          
          for (var i=0; i<elts.length; i++) {
            var elt = elts[i];
            if (!elt) continue;
//            IdentitySelectorDiag.logMessage("getEmbeddedOpenidElement", "elt.name: " + elt.name);
//            IdentitySelectorDiag.logMessage("getEmbeddedOpenidElement", "elt.id: " + elt.id);
//            IdentitySelectorDiag.logMessage("getEmbeddedOpenidElement", "elt.value: " + elt.value);
            if (!elt.value) continue;
            if (elt.value == "") continue;
            var type = elt.type;
//            IdentitySelectorDiag.logMessage("getEmbeddedOpenidElement", "type: " + type);
            if ("text" != type) continue;
            var name = elt.name;
            if (name) {
              var lcname = name.toLowerCase();
//              IdentitySelectorDiag.logMessage("getEmbeddedOpenidElement", "name: " + name);
              if ("openid_identifier" == lcname) {
                return elt;
              }
              if ("rawOpenId" == name) { // blogger.com
                return elt;
              }
            }
            var id = elt.id;
            if (id) {
              var lcid = id.toLowerCase();
//              IdentitySelectorDiag.logMessage("getEmbeddedOpenidElement", "id: " + id);
              if ("openid_identifier" == lcid) {
                return elt;
              }
              if ("rawOpenId" == id) { // blogger.com
                return elt;
              }
            }
          }

          return null;
        },
       
        // ***********************************************************************
        // Method: getTrustedCertList
        // ***********************************************************************
       
        getTrustedCertList : function()
        {
                var certListBuf = "";
               
                try
                {
                        var certDb = Cc[ nsX509CertDB].
                                                                getService( nsIX509CertDB);
                        var certCache = Cc[ nsNSSCertCache].
                                                                createInstance( nsINSSCertCache);
                        var caTreeView;
                       
                        // Load all of the certificates
                 
                        certCache.cacheAllCerts();
                       
                        // Create a tree view that includes all of the trusted CA roots
                       
                        caTreeView = Cc[ nsCertTree].
                                                                createInstance( nsICertTree);
                        caTreeView.loadCertsFromCache( certCache,
                                                                nsIX509Cert.CA_CERT | nsIX509Cert.SERVER_CERT);
                       
                        // Iterate through the items in the tree view
                       
                        for( var iLoop = 0; iLoop < caTreeView.rowCount; iLoop++)
                        {
                                var cert = caTreeView.getCert( iLoop);
                               
                                if( cert === null)
                                {
                                        // Not all rows in the tree view are certificates.  Some are
                                        // display tags, placeholders, etc.
                                       
                                        continue;
                                }
                               
                                // Get the raw DER encoding of the certificate
                               
                                var derLen = {};
                                var rawDer = cert.getRawDER( derLen);
                               
                                // Convert the raw DER to a hex string
                               
                                var derBytes = "";
                                       
                                for( var iSubLoop = 0; iSubLoop < rawDer.length; iSubLoop++)
                                {
                                        derBytes = derBytes + String.fromCharCode( rawDer[ iSubLoop]);
                                }
                               
                                certListBuf += derBytes;
                        }
                }
                catch( e)
                {
                  IdentitySelectorDiag.reportError("onHideNotificationBox", ""+ e);
                }
               
                return( certListBuf);
        },
        
        _formSubmitObserver : {
          QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver,
                                                  Ci.nsISupportsWeakReference]),

          // nsFormSubmitObserver
          notify : function (formElement, aWindow, actionURI) {
            try { // submit form if exception
              if (typeof(IdentitySelectorDiag) == "undefined") {
                var consoleService = Components.classes[ "@mozilla.org/consoleservice;1"].
                          getService( Components.interfaces.nsIConsoleService);
                consoleService.logStringMessage( "IdentitySelector: _formSubmitObserver: " + 
                  "IdentitySelectorDiag is undefined");
                consoleService.logStringMessage( "IdentitySelector: _formSubmitObserver: " + 
                  "actionURI=" + actionURI);
                var doc = formElement.ownerDocument;
                if (doc && doc.location) {
                  consoleService.logStringMessage( "IdentitySelector: _formSubmitObserver: " + 
                    "doc.location.href=" + doc.location.href);
                }
                return true; // submit
              }
              IdentitySelectorDiag.logMessage("_formSubmitObserver", "observer notified for form submission.");
  
              // We're invoked before the content's |onsubmit| handlers, so we
              // can grab form data before it might be modified (see bug 257781).
              var continueSubmit = true;
              try {
                continueSubmit = IdentitySelector.onEarlyFormSubmit(formElement, aWindow, actionURI);
              } catch (e) {
                  IdentitySelectorDiag.logMessage("_formSubmitObserver", "Caught error in onFormSubmit: " + e);
              }
  
              return continueSubmit; // return true, or form submit will be canceled.
            } catch (ee) {
              return true;
            }
          }
      }

};

// **************************************************************************
// Desc:
// **************************************************************************

var ICProgressListener =
{
        QueryInterface : function( aIID)
        {
                if( aIID.equals( Ci.nsIWebProgressListener) ||
                         aIID.equals( Ci.nsISupportsWeakReference) ||
                         aIID.equals( Ci.nsISupports))
                {
                        return( this);
                }
               
                throw Components.results.NS_NOINTERFACE;
        },
       
        onStateChange : function( aProgress, aRequest, aFlag, aStatus)
        {
                var progListIFace = Ci.nsIWebProgressListener;
               
                // Log the flags
               
                IdentitySelectorDiag.logMessage( "onStateChange", "flags = " + aFlag);
               
                if( aFlag & progListIFace.STATE_IS_DOCUMENT)
                {
                        IdentitySelectorDiag.logMessage( "onStateChange", "flag & document");
                }
                       
                if( aFlag & progListIFace.STATE_IS_WINDOW)
                {
                        IdentitySelectorDiag.logMessage( "onStateChange", "flag & window");
                }
               
                if( aFlag & progListIFace.STATE_START)
                {
                        IdentitySelectorDiag.logMessage( "onStateChange", "flag & start");
                }
               
                if( aFlag & progListIFace.STATE_STOP)
                {
                        IdentitySelectorDiag.logMessage( "onStateChange", "flag & stop");
                }
               
                // Process the document.  The 'STOP' state isn't reached until after
                // the page is fully loaded and all onload events have completed.
                // We need to re-process the page in case an onload event added
                // information card elements or objects to the page.  An example of
                // a page that does this is login.live.com.
               
                if( aFlag & progListIFace.STATE_STOP)
                {
                        if( aFlag & progListIFace.STATE_IS_WINDOW)
                        {
                                IdentitySelectorDiag.logMessage( "onStateChange",
                                        "stop status code = " + aStatus);
                                       
                                if( aStatus === 0)
                                {
                                        // Process any information card items
                                       
                                        try
                                        {
                                                IdentitySelector.processICardItems(
                                                        aProgress.DOMWindow.document, true);
                                        }
                                        catch( e)
                                        {
                                          IdentitySelectorDiag.reportError("onStateChange",  ""+e);
                                        }
                                }
                        }
                }
               
                return( 0);
        },
       
        onLocationChange : function( aProgress, aRequest, aURI)
        {
                // This fires when a load event has been confirmed or when the
                // user switches tabs.  At this point, Firefox has created a skeletal
                // document into which the source document will be loaded.  This is
                // where we run our global intercept script.
               
                try
                {
                        IdentitySelector.runInterceptScript( aProgress.DOMWindow.document);
                }
                catch( e)
                {
                        IdentitySelectorDiag.debugReportError( "onLocationChange", e);
                }
               
                return( 0);
        },
       
        onProgressChange : function()
        {
                return( 0);
        },
       
        onStatusChange : function()
        {
                return( 0);
        },
       
        onSecurityChange : function()
        {
                return( 0);
        },
       
        onLinkIconAvailable : function()
        {
                return( 0);
        }
};

// **************************************************************************
// Desc: Startup and shutdown
// **************************************************************************

try
{
    window.addEventListener("dragdrop", InformationCardDragAndDrop.onWindowDragDrop, false);

    window.addEventListener("ICObjectLoaded",InformationCardDragAndDrop.onICardObjectLoaded, false); 

        window.addEventListener( "load",
                IdentitySelector.onInstall, false);
               
        window.addEventListener( "unload",
                IdentitySelector.onUninstall, false);
}
catch( e)
{
        IdentitySelectorDiag.reportError( "window.addEventListener", e);
}